(function($) {

    Drupal.wysiwyg.editor.init.ckeditor = function(settings) {
        // Plugins must only be loaded once. Only the settings from the first format
        // will be used but they're identical anyway.
        var registeredPlugins = {};
        for (var format in settings) {
            if (Drupal.settings.wysiwyg.plugins[format]) {
                // Register native external plugins.
                // Array syntax required; 'native' is a predefined token in JavaScript.
                for (var pluginName in Drupal.settings.wysiwyg.plugins[format]['native']) {
                    if (!registeredPlugins[pluginName]) {
                        var plugin = Drupal.settings.wysiwyg.plugins[format]['native'][pluginName];
                        CKEDITOR.plugins.addExternal(pluginName, plugin.path, plugin.fileName);
                        registeredPlugins[pluginName] = true;
                    }
                }
                // Register Drupal plugins.
                for (var pluginName in Drupal.settings.wysiwyg.plugins[format].drupal) {
                    if (!registeredPlugins[pluginName]) {
                        Drupal.wysiwyg.editor.instance.ckeditor.addPlugin(pluginName, Drupal.settings.wysiwyg.plugins[format].drupal[pluginName], Drupal.settings.wysiwyg.plugins.drupal[pluginName]);
                        registeredPlugins[pluginName] = true;
                    }
                }
            }
        }
    };


    /**
     * Attach this editor to a target element.
     */
    Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
        // Apply editor instance settings.
        CKEDITOR.config.customConfig = '';

        settings.on = {
            instanceReady: function(ev) {
                var editor = ev.editor;
                // Get a list of block, list and table tags from CKEditor's XHTML DTD.
                // @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting.
                var dtd = CKEDITOR.dtd;
                var tags = CKEDITOR.tools.extend({}, dtd.$block, dtd.$listItem, dtd.$tableContent);
                // Set source formatting rules for each listed tag except <pre>.
                // Linebreaks can be inserted before or after opening and closing tags.
                if (settings.apply_source_formatting) {
                    // Mimic FCKeditor output, by breaking lines between tags.
                    for (var tag in tags) {
                        if (tag == 'pre') {
                            continue;
                        }
                        this.dataProcessor.writer.setRules(tag, {
                            indent: true,
                            breakBeforeOpen: true,
                            breakAfterOpen: false,
                            breakBeforeClose: false,
                            breakAfterClose: true
                        });
                    }
                }
                else {
                    // CKEditor adds default formatting to <br>, so we want to remove that
                    // here too.
                    tags.br = 1;
                    // No indents or linebreaks;
                    for (var tag in tags) {
                        if (tag == 'pre') {
                            continue;
                        }
                        this.dataProcessor.writer.setRules(tag, {
                            indent: false,
                            breakBeforeOpen: false,
                            breakAfterOpen: false,
                            breakBeforeClose: false,
                            breakAfterClose: false
                        });
                    }
                }
            },
            pluginsLoaded: function(ev) {
                // Override the conversion methods to let Drupal plugins modify the data.
                var editor = ev.editor;
                if (editor.dataProcessor && Drupal.settings.wysiwyg.plugins[params.format]) {
                    editor.dataProcessor.toHtml = CKEDITOR.tools.override(editor.dataProcessor.toHtml, function(originalToHtml) {
                        // Convert raw data for display in WYSIWYG mode.
                        return function(data, fixForBody) {
                            for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
                                if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
                                    data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
                                    data = Drupal.wysiwyg.instances[params.field].prepareContent(data);
                                }
                            }
                            return originalToHtml.call(this, data, fixForBody);
                        };
                    });
                    editor.dataProcessor.toDataFormat = CKEDITOR.tools.override(editor.dataProcessor.toDataFormat, function(originalToDataFormat) {
                        // Convert WYSIWYG mode content to raw data.
                        return function(data, fixForBody) {
                            data = originalToDataFormat.call(this, data, fixForBody);
                            for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
                                if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
                                    data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
                                }
                            }
                            return data;
                        };
                    });
                }
            },
            selectionChange: function(event) {
                var pluginSettings = Drupal.settings.wysiwyg.plugins[params.format];
                if (pluginSettings && pluginSettings.drupal) {
                    $.each(pluginSettings.drupal, function(name) {
                        var plugin = Drupal.wysiwyg.plugins[name];
                        if ($.isFunction(plugin.isNode)) {
                            var node = event.data.selection.getSelectedElement();
                            var state = plugin.isNode(node ? node.$ : null) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
                            event.editor.getCommand(name).setState(state);
                        }
                    });
                }
            },
            focus: function(ev) {
                Drupal.wysiwyg.activeId = ev.editor.name;
            }
        };

        // Attach editor.
        CKEDITOR.replace(params.field, settings);
    };

    /**
     * Detach a single or all editors.
     *
     * @todo 3.x: editor.prototype.getInstances() should always return an array
     *   containing all instances or the passed in params.field instance, but
     *   always return an array to simplify all detach functions.
     */
    Drupal.wysiwyg.editor.detach.ckeditor = function(context, params) {
        if (typeof params != 'undefined') {
            var instance = CKEDITOR.instances[params.field];
            if (instance) {
                instance.destroy();
            }
        }
        else {
            for (var instanceName in CKEDITOR.instances) {
                CKEDITOR.instances[instanceName].destroy();
            }
        }
    };

    Drupal.wysiwyg.editor.instance.ckeditor = {
        addPlugin: function(pluginName, settings, pluginSettings) {
            CKEDITOR.plugins.add(pluginName, {
                // Wrap Drupal plugin in a proxy pluygin.
                init: function(editor) {
                    if (settings.css) {
                        editor.on('mode', function(ev) {
                            if (ev.editor.mode == 'wysiwyg') {
                                // Inject CSS files directly into the editing area head tag.
                                $('head', $('#cke_contents_' + ev.editor.name + ' iframe').eq(0).contents()).append('<link rel="stylesheet" href="' + settings.css + '" type="text/css" >');
                            }
                        });
                    }
                    if (typeof Drupal.wysiwyg.plugins[pluginName].invoke == 'function') {
                        var pluginCommand = {
                            exec: function(editor) {
                                var data = {format: 'html', node: null, content: ''};
                                var selection = editor.getSelection();
                                if (selection) {
                                    data.node = selection.getSelectedElement();
                                    if (data.node) {
                                        data.node = data.node.$;
                                    }
                                    if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
                                        if (CKEDITOR.env.ie) {
                                            data.content = selection.getNative().createRange().text;
                                        }
                                        else {
                                            data.content = selection.getNative().toString();
                                        }
                                    }
                                    else if (data.node) {
                                        // content is supposed to contain the "outerHTML".
                                        data.content = data.node.parentNode.innerHTML;
                                    }
                                }
                                Drupal.wysiwyg.plugins[pluginName].invoke(data, pluginSettings, editor.name);
                            }
                        };
                        editor.addCommand(pluginName, pluginCommand);
                    }
                    editor.ui.addButton(pluginName, {
                        label: settings.iconTitle,
                        command: pluginName,
                        icon: settings.icon
                    });

                    // @todo Add button state handling.
                }
            });
        },
        prepareContent: function(content) {
            // @todo Don't know if we need this yet.
            return content;
        },
        insert: function(content) {
            content = this.prepareContent(content);
            CKEDITOR.instances[this.field].insertHtml(content);
        }
    };

})(jQuery);
